In [7]:
import types
import pickle
import copy_reg
In [9]:
# why functions with closures can't be serialised
# this generates a Cell object, which represents a variable in a closure
create_cell = lambda x: (lambda: x).func_closure[0]
# create our function with a closure
def mk ():
x = 5
y = mk
def f ():
return x, y
return f
f = mk()
# this...
g = pickle.loads(pickle.dumps(CannedFunction(f))).getObject()
# ...essentially does (would do) this
co = f.func_code
co = types.CodeType(co.co_argcount, co.co_nlocals, co.co_stacksize,
co.co_flags, co.co_code, co.co_consts, co.co_names,
co.co_varnames, co.co_filename, co.co_name,
co.co_firstlineno, co.co_lnotab)
closure = f.func_closure
if closure is not None:
closure = tuple(create_cell(obj) for obj in f.func_closure)
# recreating the code object (co) makes the function 'want' a 0-length
# closure
g = types.FunctionType(co, f.func_globals, f.func_name, f.func_defaults,
closure)
In [10]:
# some modified function serialisation stuff from ipython
def code_ctor(*args):
return types.CodeType(*args)
def reduce_code(co):
#if co.co_freevars or co.co_cellvars:
#raise ValueError("Sorry, cannot pickle code objects with closures")
args = [co.co_argcount, co.co_nlocals, co.co_stacksize,
co.co_flags, co.co_code, co.co_consts, co.co_names,
co.co_varnames, co.co_filename, co.co_name, co.co_firstlineno,
co.co_lnotab]
if sys.version_info[0] >= 3:
args.insert(1, co.co_kwonlyargcount)
return code_ctor, tuple(args)
copy_reg.pickle(types.CodeType, reduce_code)
class CannedFunction (object):
def __init__(self, f):
self._checkType(f)
self.code = f.func_code
self.closure = [cell.cell_contents for cell in f.func_closure]
self.defaults = f.func_defaults
self.module = f.__module__ or '__main__'
self.__name__ = f.__name__
def _checkType(self, obj):
assert isinstance(obj, types.FunctionType), "Not a function type"
def getObject(self, g=None):
# try to load function back into its module:
if not self.module.startswith('__'):
try:
__import__(self.module)
except ImportError:
pass
else:
g = sys.modules[self.module].__dict__
if g is None:
g = globals()
closure = tuple(create_cell(obj) for obj in self.closure)
newFunc = types.FunctionType(self.code, g, self.__name__,
self.defaults, closure)
return newFunc